namespace IconPatcher
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.IO;
    using System.Runtime.InteropServices;

    using IconPatcher.Annotations;

    internal static class Helpers
    {
        public static readonly List<DllInformation> DllList = new List<DllInformation>();

        static Helpers()
        {
            LoadDllList();
        }

        [Conditional("DEBUG")]
        [StringFormatMethod("Message")]
        private static void DebugInfo(string message, params object[] args)
        {
            Console.WriteLine(message, args);
        }

        private static void LoadDllList()
        {
            // Common7\IDE
            AddDllToList(false, @"Common7\IDE\msenv.dll", 303, 307, 308, 309, 326, 327, 328, 1002, 1004, 1268, 1423, 1425, 1427, 6000, 6100, 7003);

            // Common7\IDE\1033 - these files exist no matter what languages are installed
            AddDllToList(false, @"Common7\IDE\1033\Microsoft.VisualStudio.SilverlightUI.dll", "IDB_IMAGELIST");
            AddDllToList(false, @"Common7\IDE\1033\WindowsFormsIntegration.PackageUI.dll", 2001);

            // Common7\IDE\{language}
            AddDllToList(true, @"Common7\IDE\{language}\cmddefui.dll",
                2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
                2012, 2013, 2014, 2015, 2016, 2017,
                2020,
                2022, 2023, 2024, 2025, 2026, 2027, 2028, 2029, 2030, 2031, 2032, 2033, 2034, 2035
            );
            AddDllToList(true, @"Common7\IDE\{language}\Microsoft.VisualStudio.DesignUI.dll", 10100);
            AddDllToList(true, @"Common7\IDE\{language}\Microsoft.VisualStudio.EditorsUI.dll", 2);
            AddDllToList(true, @"Common7\IDE\{language}\Microsoft.VisualStudio.SilverlightUI.dll", "IDB_IMAGELIST");
            AddDllToList(true, @"Common7\IDE\{language}\Microsoft.VisualStudio.Web.ApplicationUI.dll", "IDB_IMAGELIST");
            AddDllToList(true, @"Common7\IDE\{language}\Microsoft.VisualStudio.Windows.FormsUI.dll",
                2000, 2001, 2002, 2003, 2004,
                2100, 2101, 2102, 2103, 2104, 2105, 2106, 2107, 2108, 2109, 2110, 2111, 2112, 2113,
                2200, 2201, 2202, 2203, 2204, 2205,
                2300, 2301, 2302, 2303, 2304,
                2400, 2401, 2402, 2403, 2404,
                2500, 2501, 2502, 2503, 2504,
                2600, 2601, 2602, 2603, 2604, 2605,
                2700, 2701, 2702, 2703, 2704, 2705, 2706, 2707, 2708, 2709, 2710, 2711, 2712, 2713, 2714, 2715, 2716, 2717, 2718, 2719, 2720
            );
            AddDllToList(true, @"Common7\IDE\{language}\Microsoft.VSDesignerUI.dll",
                2, 5, 6, 11, 12, 13, 14, 21, 22, 23, 24
            );
            AddDllToList(true, @"Common7\IDE\{language}\Microsoft.Workflow.VSDesignerUI.dll",
                123, 124, 125, 126, 127, 128, 129
            );
            AddDllToList(true, @"Common7\IDE\{language}\msenvui.dll",
                300, 304, 305, 6650, 6651
            );
            AddDllToList(true, @"Common7\IDE\{language}\msvbideui.dll", 109, 110);
            AddDllToList(true, @"Common7\IDE\{language}\WindowsFormsIntegration.PackageUI.dll", 2001);
            AddDllToList(true, @"Common7\IDE\{language}\WorkflowProjectUI.dll", 136);

            // Common7\IDE\VS SCC
            AddDllToList(false, @"Common7\IDE\VS SCC\VssProvider.dll", 1, 4003, 6700, 6701, 6707);

            // Common7\IDE\VS SCC\{language}
            AddDllToList(true, @"Common7\IDE\VS SCC\{language}\Tfsproviderui.dll", 1, 4003, 6700, 6701, 6707);
            AddDllToList(true, @"Common7\IDE\VS SCC\{language}\VssProviderui.dll", 1, 4003, 6700, 6701, 6707);

            // Common7\Packages
            AddDllToList(false, @"Common7\Packages\htmled.dll",
                210, 212 /* html outline view */, 213, 216, 220
            );
            AddDllToList(false, @"Common7\Packages\mswebprj.dll", 333);
            AddDllToList(false, @"Common7\Packages\webdirprj.dll", 523, 4617);

            // Common7\Packages\{language}
            AddDllToList(true, @"Common7\Packages\{language}\csspkgui.dll", 200, 4000, 4100); // css outline view
            AddDllToList(true, @"Common7\Packages\{language}\fpedsat.dll", "IDB_SCRIPT_TREE_IMAGES");
            AddDllToList(true, @"Common7\Packages\{language}\htmledui.dll", 217, 218, 219, 221);

            // Common7\Packages\Debugger\{language}
            AddDllToList(true, @"Common7\Packages\Debugger\{language}\vsdebugui.dll", 295, 296, 297, 299, 300, 301, 302, 308, 5123, 5124, 5125);

            // VC#\VCSPackages
            AddDllToList(false, @"VC#\VCSPackages\csproj.dll", 333);

            // VC#\VCSPackages\1033 - these files exist no matter what languages are installed
            AddDllToList(false, @"VC#\VCSPackages\1033\cslangsvcui.dll", 248, 249);

            // VC#\VCSPackages\{language}
            AddDllToList(true, @"VC#\VCSPackages\{language}\cslangsvcui.dll", 248, 249);

            // VB\Bin
            AddDllToList(false, @"VB\Bin\msvbprj.dll", 333);
        }

        private static void AddDllToList(bool supportsCulture, string dllPath, string imageName)
        {
            DllList.Add(new DllInformation(supportsCulture, dllPath, imageName));
        }
        private static void AddDllToList(bool supportsCulture, string dllPath, params int[] imageIdList)
        {
            foreach (var imageId in imageIdList)
            {
                DllList.Add(new DllInformation(supportsCulture, dllPath, imageId));
            }
        }

        public static VisualStudioVersion ToVersion(string value)
        {
            int tmp;
            if (!int.TryParse(value, out tmp))
            {
                throw new ArgumentException("Value must be numeric.", "value");
            }

            if (!Enum.IsDefined(typeof(VisualStudioVersion), tmp))
            {
                throw new ArgumentOutOfRangeException("value");
            }

            return (VisualStudioVersion)Enum.Parse(typeof(VisualStudioVersion), value);
        }

        // is there a better way to auto-detect these paths?
        public static int GetInstallPath(VisualStudioVersion version, out string installPath)
        {
            installPath = null;

            switch (version)
            {
                case VisualStudioVersion.VS2010:
                    DebugInfo("You picked VS 2010");
                    installPath = Environment.ExpandEnvironmentVariables("%VS100COMNTOOLS%");
                    break;

                case VisualStudioVersion.VS2012:
                    DebugInfo("You picked VS 2012");
                    installPath = Environment.ExpandEnvironmentVariables("%VS110COMNTOOLS%");
                    break;

                default:
                    Console.WriteLine("'{0}' isn't a valid option.", version);
                    return 1;
            }

            if (!Directory.Exists(Environment.ExpandEnvironmentVariables(installPath)))
            {
                Console.WriteLine("Unable to find the install path for Visual Studio.");
                return 1;
            }

            installPath = new DirectoryInfo(installPath).Parent.Parent.FullName;

            return 0;
        }

        public static void ExpandAndDelete(string path)
        {
            var file = Environment.ExpandEnvironmentVariables(path);
            if (!File.Exists(file))
            {
                return;
            }

            File.Delete(file);
        }

        public static void ClearImageCache(VisualStudioVersion version)
        {
            if (version != VisualStudioVersion.VS2012)
            {
                DebugInfo("Clearing the image cache for version {0} is not supported.");
                return;
            }

            // these cache files need to be deleted in order for the toolbox to show the new icons
            ExpandAndDelete(@"%LOCALAPPDATA%\Microsoft\VisualStudio\11.0\toolbox.tbd");
            ExpandAndDelete(@"%LOCALAPPDATA%\Microsoft\VisualStudio\11.0\toolbox_reset.tbd");
            ExpandAndDelete(@"%LOCALAPPDATA%\Microsoft\VisualStudio\11.0\toolboxindex.tbd");
            ExpandAndDelete(@"%LOCALAPPDATA%\Microsoft\VisualStudio\11.0\toolboxindex_reset.tbd");

            // this file needs to be deleted in order for the general menu & toolbar icons to update
            // the file can exist in one or both locations depending on the permissions level of your user account
            foreach (var language in Program.Languages2012)
            {
                ExpandAndDelete(@"%LOCALAPPDATA%\Microsoft\VisualStudio\11.0\{language}\devenv.CTM".HanselFormat(new { language }));
                ExpandAndDelete(@"%PROGRAMDATA%\Microsoft\VisualStudio\11.0\{language}\devenv.CTM".HanselFormat(new { language }));
            }

            // ToDo: figure out what file[s] need to be cleared to force the Document Outline icons to fully refresh
        }

        public static bool IsVisualStudioRunning()
        {
            // Marshal.GetActiveObject doesn't work when UAC is enabled which is why we're using this method now
            return Process.GetProcessesByName("devenv").Length != 0;
        }

        public static string LastWin32ErrorMessage()
        {
            var exception = new Win32Exception();
            return exception.Message;
        }
    }
}